package com.whut.imservice.ws;

import com.whut.imservice.config.ServerConfig;
import com.whut.imservice.factory.EnhancedThreadFactory;
import com.whut.imservice.ws.handler.CloseIdleChannelHandler;
import com.whut.imservice.ws.handler.WebsocketRouterHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import io.netty.util.concurrent.EventExecutorGroup;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @Description: websocket服务器
 *
 * @Author: Zonghe Wang
 * @Date: 2025/5/24 19:56
 */
@Component
@RequiredArgsConstructor
public class WebsocketServer {

    private static final Logger log = LoggerFactory.getLogger(WebsocketServer.class);

    private static final int DEFAULT_SHUTDOWN_TIMEOUT = 10;
    private static final String WEBSOCKET_PATH  = "/";

    private final AtomicBoolean isRunning = new AtomicBoolean(false);
    private final ServerConfig serverConfig;
    private final WebsocketRouterHandler websocketRouterHandler;
    private final CloseIdleChannelHandler closeIdleChannelHandler;

    private ChannelFuture channelFuture;
    private EventExecutorGroup eventExecutorGroup;
    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;
    @PostConstruct
    public void start() throws InterruptedException {

        if (serverConfig.port <= 0 || serverConfig.port >= 65535) {
            log.warn("port不合法: {}", serverConfig.port);
            return;
        }

        if (!isRunning.compareAndSet(false, true)) {
            log.warn("websocket服务器已经启动");
            return;
        }

        initializeServer();
        registerShutdownHook();

        log.info("启动websocket服务器成功");
    }

    @PreDestroy
    public void preDestroy() {
        close();
    }

    private void registerShutdownHook() {
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            log.info("ShutdownHook");
            close();
        }, "WebsocketShutdownHook"));
    }

    public synchronized void close() {
        if (!isRunning.compareAndSet(true, false)) {
            return;
        }

        log.info("开始关闭websocket服务器");

        // 关闭监听通道
        if (channelFuture != null) {
            channelFuture.channel()
                    .close()
                    .awaitUninterruptibly(DEFAULT_SHUTDOWN_TIMEOUT, TimeUnit.SECONDS);
        }

        // 关闭线程组
        shutdownGroup("Boss", bossGroup);
        shutdownGroup("Worker", workerGroup);
        shutdownGroup("EventExecutor", eventExecutorGroup);

        log.info("websocket服务器已关闭");
    }

    private void shutdownGroup(String groupName, EventExecutorGroup group) {
        if (group != null && !group.isShutdown()) {
            try {
                log.debug("{} shutdown", groupName);
                group.shutdownGracefully().sync();
                log.debug("{} shutdown complete", groupName);
            } catch (InterruptedException e) {
                log.error("{} shutdown error", groupName, e);
                Thread.currentThread().interrupt();
            }
        }
    }

    private void initializeServer() throws InterruptedException {
        log.info("初始化websocket服务器");

        // 初始化业务线程池
        eventExecutorGroup = new DefaultEventExecutorGroup(
                serverConfig.userThreads,
                new EnhancedThreadFactory("WebsocketBizThreadPool")
        );

        // 配置网络线程组
        configureEventLoopGroup();

        // 初始化服务器引导
        ServerBootstrap bootstrap = new ServerBootstrap()
                .group(bossGroup, workerGroup)
                .channel(useEpoll() ? EpollServerSocketChannel.class : NioServerSocketChannel.class)
                .childHandler(new WebsocketChannelInitializer())
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                .option(ChannelOption.SO_BACKLOG, 128);
        
        // 绑定端口并启动
        channelFuture = bootstrap.bind(serverConfig.port).sync();
        startCloseFutureListener();
    }

    private void startCloseFutureListener() {
        channelFuture.channel().closeFuture().addListener(future -> {
            if (future.isSuccess()) {
                log.info("websocket channel 正常关闭");
            } else {
                log.error("websocket channel 异常关闭", future.cause());
            }
            isRunning.set(false);
        });
    }

    private class WebsocketChannelInitializer extends ChannelInitializer<SocketChannel> {
        @Override
        protected void initChannel(SocketChannel socketChannel) {
            ChannelPipeline pipeline = socketChannel.pipeline();
            
            // Http协议处理
            pipeline.addLast(new HttpServerCodec());
            pipeline.addLast(new HttpObjectAggregator(65536));
            
            // WebSocket处理
            pipeline.addLast(new WebSocketServerProtocolHandler(WEBSOCKET_PATH, null, true));
            
            // 日志和空闲检测
            pipeline.addLast(new LoggingHandler(LogLevel.DEBUG));
            pipeline.addLast(new IdleStateHandler(0, 0, serverConfig.getIdleTimes()));
            
            // 业务处理器
            pipeline.addLast(eventExecutorGroup, websocketRouterHandler);
            pipeline.addLast(closeIdleChannelHandler);
        }
    }

    private void configureEventLoopGroup() {
        if (useEpoll()) {
            bossGroup = new EpollEventLoopGroup(
                    serverConfig.bossThreads,
                    new EnhancedThreadFactory("WebsocketEpollBoss")
            );
            workerGroup = new EpollEventLoopGroup(
                    serverConfig.workerThreads,
                    new EnhancedThreadFactory("WebsocketEpollWorker")
            );
            log.debug("使用epoll");
        } else {
            bossGroup = new NioEventLoopGroup(
                    serverConfig.bossThreads,
                    new EnhancedThreadFactory("WebsocketNioBoss")
            );
            workerGroup = new NioEventLoopGroup(
                    serverConfig.workerThreads,
                    new EnhancedThreadFactory("WebsocketNioWorker")
            );
            log.debug("使用nio");
        }
    }

    private boolean useEpoll() {
        return Epoll.isAvailable() && serverConfig.useEpoll;
    }

}
